package cn.dduan.fool.design.pattern.behavior.iterator;

import java.util.ArrayList;
import java.util.List;

/**
 * 迭代器模式
 * <p>
 * 核心：
 * 1、提供一种访问集合中的各个元素，而不暴露其内部表示的方法
 * 2、将在元素之间游走的职责交给迭代器，而不是集合对象，从而简化集合容器的实现，让集合容器专注于在它所应该专注的事情上，更加符合单一职责原则
 * <p>
 * 优点：
 * 1、迭代器模式简化了集合的接口，迭代子具备遍历功能，这样集合的接口就不需要重新实现遍历功能
 * 2、每一个聚集对象都可以有一个或多个迭代子对象，每一个迭代子的迭代状态可以是彼此独立的。因此，一个聚集对象可以同时有几个迭代在进行之中
 * 3、由于遍历算法被封装在迭代子角色里面，因此迭代的算法可以独立于聚集角色变化
 * 4、更好的封装性，访问一个集合对象的元素，无需暴露容器内部表示
 * <p>
 * 缺点：
 * 1、迭代器模式将存储数据和遍历数据的职责分离，增加新的聚合类需要对应增加新的迭代器类，类的个数成对增加，这在一定程度上增加了系统的复杂性
 * 2、对于比较简单的遍历，使用迭代器模式显得较为繁琐，比如ArrayList直接就可以用for循环+get() 方法来遍历
 * 3、抽象迭代器的设计难度较大，需要充分考虑到系统将来的扩展
 * <p>
 * 适用场景：
 * 1、访问一个集合对象的内容而无须暴露它的内部表示
 * 2、需要为集合对象提供多种遍历方式
 * 3、为遍历不同的聚合结构提供一个统一的接口
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
public class IteratorPattern {
    public static void main(String[] args) {
        TVChanneMenu tvMenu = new TVChanneMenu();
        FilmMenu filmMenu = new FilmMenu();

        MainMenu mainMenu = new MainMenu(tvMenu, filmMenu);
        mainMenu.printMenu();
    }
}

/**
 * 主菜单
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class MenuItem {
    private String name;
    private String description;
    private int channe;

    public MenuItem(int channe, String name, String description) {
        this.name = name;
        this.description = description;
        this.channe = channe;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getChanne() {
        return channe;
    }

    public void setChanne(int channe) {
        this.channe = channe;
    }

}

/**
 * 迭代器接口
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
interface Iterator {
    boolean hasNext();

    Object next();
}

/**
 * 具体迭代器：电影节目
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class FilmMenuIterator implements Iterator {
    MenuItem[] menuItems;
    int position = 0;

    public FilmMenuIterator(MenuItem[] menuItems) {
        this.menuItems = menuItems;
    }

    @Override
    public boolean hasNext() {
        if (position > menuItems.length - 1 || menuItems[position] == null) {
            return false;
        }
        return true;
    }

    @Override
    public Object next() {
        MenuItem menuItem = menuItems[position];
        position++;
        return menuItem;
    }
}

/**
 * 具体迭代器：电视界面
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class TVChanneMenuIterator implements Iterator {

    List<MenuItem> menuItems;
    int position = 0;

    public TVChanneMenuIterator(List<MenuItem> menuItems) {
        this.menuItems = menuItems;
    }

    @Override
    public boolean hasNext() {
        if (position > menuItems.size() - 1 || menuItems.get(position) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = menuItems.get(position);
        position++;
        return menuItem;
    }
}

/**
 * 迭代器方法接口
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
interface TelevisionMenu {
    public void addItem(int channe, String name, String description);

    public Iterator createIrerator();
}

/**
 * 具体迭代器方法
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class FilmMenu implements TelevisionMenu {
    // 菜单最大长度
    static final int MAX_ITEMS = 5;
    MenuItem[] menuItems;
    int numberOfItems = 0;

    /**
     * 构造函数完成初始化
     */
    public FilmMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem(1, "绝世天劫", "这是布鲁斯威利斯主演的...");
        addItem(2, "达芬奇密码", "这是我最喜欢的电影之一...");
        addItem(3, "黑客帝国123", "不知道你能够看懂不???");
        addItem(4, "我的女友是机器人", "一部我不反感的经典爱情电影...");
        addItem(5, "肖申克的救赎", "自由，幸福，离你有多远");
    }

    /**
     * @desc 将电影解决添加到菜单项中
     */
    @Override
    public void addItem(int channe, String name, String description) {
        MenuItem tvmenuiItem = new MenuItem(channe, name, description);
        //判断数组是否越界
        if (numberOfItems > MAX_ITEMS) {
            System.out.println("不好意思，菜单满了....");
        } else {
            menuItems[numberOfItems] = tvmenuiItem;
            numberOfItems++;
        }
    }

    @Override
    public Iterator createIrerator() {
        return new FilmMenuIterator(menuItems);
    }
}

/**
 * 具体迭代器方法
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class TVChanneMenu implements TelevisionMenu {
    List<MenuItem> menuItems;

    /**
     * 构造函数完成初始化
     */
    public TVChanneMenu() {
        menuItems = new ArrayList<MenuItem>();
        addItem(1, "CCTV-1", "This is CCTV-1");
        addItem(2, "CCTV-2", "This is CCTV-2");
        addItem(3, "CCTV-3", "This is CCTV-3");
        addItem(4, "CCTV-4", "This is CCTV-4");
        addItem(5, "CCTV-5", "This is CCTV-5");
    }

    /**
     * @desc 将电视频道节目添加菜单集合中
     */
    @Override
    public void addItem(int channe, String name, String description) {
        MenuItem tvMenuItem = new MenuItem(channe, name, description);
        menuItems.add(tvMenuItem);
    }

    @Override
    public Iterator createIrerator() {
        return new TVChanneMenuIterator(menuItems);
    }
}

/**
 * 主菜单
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class MainMenu {
    TelevisionMenu tvMenu;
    FilmMenu filmMenu;

    public MainMenu(TelevisionMenu tvMenu, FilmMenu filmMenu) {
        this.tvMenu = tvMenu;
        this.filmMenu = filmMenu;
    }

    public void printMenu() {
        Iterator tvIterator = tvMenu.createIrerator();
        Iterator filmIterator = filmMenu.createIrerator();

        System.out.println("电视节目有:");
        printMenu(tvIterator);
        System.out.println("----------------------------------------------------------------");
        System.out.println("电影节目有:");
        printMenu(filmIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.print("channe:" + menuItem.getChanne() + ",  ");
            System.out.print("name:" + menuItem.getName() + ",  ");
            System.out.println("description :" + menuItem.getDescription());
        }
    }
}